home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / dataval.exe / DATAVALD.TXT < prev    next >
Encoding:
Text File  |  1991-09-10  |  25.3 KB  |  465 lines

  1. Data Validation Techniques
  2. In Turbo Vision
  3.  
  4.  
  5. by Danny Thorpe
  6.  
  7. presented at the 
  8. Borland Languages Conference
  9. April 1991
  10.  
  11.  
  12. Data Validation Techniques in Turbo Vision
  13. Turbo Vision, the new object-oriented event-driven application 
  14. framework introduced in Borland's Turbo Pascal 6.0, has generated quite 
  15. a bit of excitement in programming circles.  With Turbo Vision, 
  16. programmers finally have the tools and application framework necessary 
  17. to create programs that can truly benefit from the OOP methodology.  
  18. Programmers benefit from the variety of objects in Turbo Vision that can 
  19. be molded and extended into a multitude of application-specific dialog 
  20. boxes, windows, and other views.
  21. Turbo Vision is a multi-faceted environment.  At a superficial 
  22. level, it is a library of interrelated objects for managing the screen 
  23. display.  At another level, though, Turbo Vision is also an application 
  24. framework which defines a particular model of how a program should work, 
  25. both internally and visually.  It is this model and its framework 
  26. embodiment that provides the glue which ties the objects in the library 
  27. together into a functional society of objects.  A large portion of the 
  28. increased programmer productivity that can be realized with Turbo Vision 
  29. is due to this model as well:  the application framework you inherit 
  30. takes care of the actual mechanics of the program, so the programmer has 
  31. just to plug in modules and create cross-connections between objects.
  32. While many programmers appear to accept the code changes necessary 
  33. to move to Turbo Vision, some are reluctant to also embrace the 
  34. programming model that is an important part of Turbo Vision.  Without 
  35. this model, one can wind  up using Turbo  Vision objects in ways that 
  36. work against the grain of the programming model, creating unnecessary 
  37. complexity and frustration for the programmer.
  38. A large portion of any program involves feeding in data for the 
  39. program to operate on.  Traditional techniques for interactive data 
  40. entry are particularly difficult for programmers to give up when 
  41. learning Turbo Vision.  This paper will discuss some of the philosophy 
  42. behind Turbo Vision, the mechanics of various internal operations in 
  43. Turbo Vision, and why some of the old data entry techniques won't work 
  44. anymore.  Alternate approaches to data entry and validation more in line 
  45. with Turbo Vision model will be introduced and demonstrated.
  46.  
  47.  
  48. The Turbo Vision Model
  49. The Turbo Vision user interface is based upon the IBM System 
  50. Application Architecture, Common User Access specification.  CUA 
  51. provides guidelines that determined the behavior of the mouse, input 
  52. lines, push-buttons, and the choice of keys for particular actions, such 
  53. as Esc to cancel a dialog and Enter to select the default button of the 
  54. dialog.  
  55. A driving force in the Turbo Vision programming model is the idea 
  56. that the user of the program should be in control of the program at all 
  57. times.  Let the user do what he wants to do, within reason.  This 
  58. doesn't sound very remarkable at first, but when you consider how few 
  59. programs on the market allow the user this freedom, you begin to realize 
  60. that an unnecessary evil has been lurking in computer software: too many 
  61. restrictions are imposed upon the user by the program/programmer, to the 
  62. point that the software is in control of the user.
  63. Of course, a program needs structure in order to organize its 
  64. information into a meaningful form.  The user needs the information to 
  65. be structured, too, in order to understand the material.  The difficulty 
  66. comes in finding the fine line between the amount of structure needed by 
  67. the user and the amount of structure that begins to stifle the user with 
  68. unnecessary conditions and requirements.  Programs frequently wind up on 
  69. the stifling end of the scale simply because they're easier to write and 
  70. manage.  With traditional programming tools it is difficult to construct 
  71. a well-balanced, flexible program that considers all contingencies the 
  72. user may wish to embark upon.
  73. With Turbo Vision, you inherit a well-balanced, flexible program 
  74. architecture and then fill in the details of what your application is to 
  75. do.  At the lowest levels in the object hierarchy, Turbo Vision views 
  76. have the ability to be manipulated by the user in a multitude of ways.  
  77. But the programmer doesn't have to bend over backwards in Turbo Vision 
  78. to accomplish this user freedom.  All the programmer has to do is work 
  79. within the Turbo Vision model to extend the behavior of view objects 
  80. that already know how to interact with the user.
  81. The Turbo Vision model provides an environment based on autonomy 
  82. and independence.  The careful integration of OOP and event driven 
  83. programming provides the programmer with a rich set of tools and opens 
  84. doors to programming solutions nearly unattainable by traditional 
  85. methods.  Working in this model actually provides the programmer more 
  86. flexibility in expression and more opportunity to bring sophisticated 
  87. applications to completion than traditional programming tools can offer.
  88.  
  89.  
  90.  
  91. Traditional Validation Techniques
  92. As-You-Type Validation
  93. One method of ensuring an input field contains "clean" data is to 
  94. monitor each keystroke the user types.  Keys can be checked by position 
  95. within the input field if the validation is controlled by a template.  
  96. Formatting telephone number fields with a template like (999) 999-9999, 
  97. where '9' means 'accept any number in this position' is a common use of 
  98. as-you-type validation.  If a non-numeric key is pressed, the key 
  99. monitor can beep or provide some other non-intrusive indicator of an 
  100. error.  The other characters in the template can be treated just as 
  101. place-holders, so the user doesn't have to enter the parentheses for 
  102. every phone number, for example.
  103. As-you-type validation is good for position-sensitive formatting 
  104. of data, such as telephone numbers or dates, and for character 
  105. conversions, such as converting all characters entered in a field to 
  106. uppercase as they are typed.  As-you-type is difficult to use when 
  107. numbers or whole words need to be validated before being accepted, 
  108. because you don't really know what the user intends until the user is 
  109. finished typing, backspacing, and correcting misspellings.
  110. Templates are easy to implement in Turbo Vision:  make a 
  111. descendent of a TInputLine object and extend the HandleEvent method to 
  112. test each keystroke received by that view.  Acceptable keystrokes can be 
  113. passed to the inherited HandleEvent for default processing, and 
  114. unacceptable keys can be discarded with a beep and/or non-intrusive 
  115. error message.  
  116. As-You-Leave Validation
  117. Another opportunity to test the data is when the user moves the 
  118. cursor out of the field, presumably to the next field on the screen.  
  119. This method assumes that when the user hits the Tab key or Enter key to 
  120. move to the next field, the user is finished keying this field, and so 
  121. it is fair game for validation.  Validation is performed before the 
  122. program moves focus to the next field.  
  123. If the contents of the field are unacceptable, it is common for an 
  124. error message to be displayed, and the user is put back in the bad field 
  125. to reenter it.  This is where a poorly designed data entry system can be 
  126. really mean to the user - what if the user decides to cancel the whole 
  127. data entry operation?  It would be the job of every field to watch for a 
  128. particular "cancel" key, such as F1 or Esc, and shut down the data entry 
  129. mode when such a keystroke occurs.
  130. Data can be tested as the user leaves a field in Turbo Vision.  
  131. However, the traditional response of forcing the user back to a field 
  132. after the user has indicated he wants to go elsewhere conflicts with the 
  133. Turbo Vision model.  Consequently, this behavior is difficult to 
  134. implement in Turbo Vision, mainly because it requires different 
  135. assumptions about the user and environment than Turbo Vision assumes.  A 
  136. detailed explanation of the focus change mechanism is covered in the 
  137. next section.
  138. As-you-leave validation assumes that when the user leaves the 
  139. field, the field is ready to be validated.  This is a safe assumption in 
  140. a single-window keyboard-only program.   But Turbo Vision's easy access 
  141. to multi-windowed text and mouse support invalidates this assumption, 
  142. because the user could be leaving the field to go to an entirely 
  143. different window, such as a text editor or calculator.  
  144. Assume for a moment that a programmer has implemented an As-you-
  145. leave, force-back-if-invalid validation scheme for Turbo Vision views.  
  146. Picture a dialog box with a few data entry lines, a close-icon on the 
  147. frame, an Ok button, and a Cancel button.  The user enters invalid data 
  148. in one of the input fields and presses the tab key to move to the next 
  149. input field.  The validation routines kick in when the current field is 
  150. about to lose focus, detect the invalid entry, and prevent the user from 
  151. moving to the next input field until valid data is entered.  A mild 
  152. frustration for the user, perhaps, but a liveable situation.
  153. Let's say the user enters invalid data in the input field, then 
  154. clicks the mouse on the Cancel button in the dialog.  This will cause a 
  155. focus change from the current input field to the dialog button.  Since 
  156. controls in a dialog usually operate completely oblivious of each other, 
  157. the input field has no way of knowing where the focus is going to when 
  158. it loses focus.  In this scenario, the validation routines would still 
  159. force the user back to the input field until the field contained valid 
  160. data.  What's the point of having a Cancel button if you can't use it 
  161. when you need it most?  This is an unacceptable situation.
  162. For the sake of argument, let's say the programmer recodes all the 
  163. input field objects with throwback validation to recognize a focus 
  164. change to a special Cancel button and allows the cancel to take place.  
  165. The user is appeased for the moment, but what happens when the user 
  166. demands a new button on that dialog?  More special casing, more hidden 
  167. interdependencies?  
  168. The programmer has committed one of the worst sins of object-
  169. oriented programming - special casing in general purpose objects - which 
  170. quickly defeats many of the advantages of OOP:  independence, 
  171. flexibility, maintainability.  As soon as the user demands a new button 
  172. on that dialog, the programmer must recode all the input field objects 
  173. related to that throwback validation scheme, expending more time and 
  174. energy on an old problem, possibly destabilizing the entire application, 
  175. and never really solving the problem to begin with.  All this, just to 
  176. add a simple button.
  177. As-you-leave validation is not improper in Turbo Vision, if it is 
  178. done non-intrusively.  A simple As-you-leave validation response to bad 
  179. data is to simply change the color of the field.  The user will see the 
  180. possible error and can address the problem as he sees fit.
  181. When-You're-Done Validation
  182. A third opportunity to test the data is when the screen or record 
  183. is complete and the user indicates he is finished entering or editing 
  184. the data.  All forms of validation can be performed at this stage 
  185. because the data is complete and the user has indicated the data is 
  186. complete.  No assumptions to tippy-toe around here.  If one of the 
  187. fields contains unacceptable data, the user can be returned to the bad 
  188. field with an error message.  The problem of providing the user an 
  189. escape still exists.
  190. When-you're-done validation is Turbo Vision's default validation 
  191. mode.  When the user exits a dialog, the Valid method of every view in 
  192. the dialog is called.  If any Valid method returns False, then the 
  193. dialog will not close.  If the user cancels the dialog, no validation 
  194. calls are made and the data in the dialog is discarded.
  195. This data validation method is the most flexible in Turbo Vision.  
  196. One can extend the behavior of TDialog, for example, to perform data 
  197. validation and respond to errors in a variety of ways.  As detailed 
  198. later in this paper, a dialog can be made to find which view is invalid 
  199. and return the user to that view for corrections.  And by validating the 
  200. data when the user indicates he's ready, we avoid all the problems 
  201. associated with As-you-leave validation in a multi-windowed environment.
  202.  
  203.  
  204. Focus Change Internals
  205. When a user hits the Tab key to move from one dialog control to 
  206. another, a rapid sequence of events and object method calls occurs to 
  207. transfer the focus of the dialog from one view to another.  Programmers 
  208. tend to attack this focus change sequence when attempting to force Turbo 
  209. Vision to return focus to a bad field in an As-you-leave data 
  210. validation.  This section describes the sequence of events in a focus 
  211. change and why it should not be aborted in mid-stream.
  212. Turbo Vision terms will be flying fast and furious in a moment.  
  213. If you are not familiar with the terms view, group, focused, and 
  214. selected, take a minute to review the terminology appendix at the end of 
  215. this paper.
  216. Press the Tab key.
  217. Let's say the user is looking at a dialog which has two input 
  218. lines.  When the user presses the Tab key, an evKeydown event is 
  219. generated, which is sent to the dialog's HandleEvent method.  The Tab 
  220. key event is passed back to ancestral handleevents and eventually gets 
  221. processed by TWindow.HandleEvent.  For a Tab key, TWindow.HandleEvent 
  222. calls TGroup.SelectNext.  
  223. TGroup.SelectNext scans through the group's (dialog's) list of 
  224. views for the next selectable view.  In this example, the next 
  225. selectable view is the second input line.  The second input line's 
  226. Select method is called, which by inheritance works out to be 
  227. TView.Select.
  228. TView.Select calls TGroup.SetCurrent to make itself (the second 
  229. input line) the current view.
  230. TGroup.SetCurrent
  231. The TGroup object contains a private method called SetCurrent that 
  232. performs the dirty work of changing the focus from one view to another.  
  233. It is a private method because the operations it performs are low-level 
  234. and are not considered safe to modify.  
  235. TGroup.SetCurrent does four things of importance, and all of them 
  236. involve setting the state flags of the two views transferring focus.  
  237. First, the currently focused view is de-focused, then de-selected.  
  238. Next, the new view to receive focus is selected, then focused.
  239. We'll trace what happens when a view changes state in a moment.  
  240. Right now, it's important to observe that there is no room for 
  241. arbitration in this rapid fire sequence of defocusing and refocusing.  
  242. View A is told to de-focus and de-select itself.  Then View B is told to 
  243. select and focus itself.  If a programmer were to modify a view's 
  244. SetState method to refuse to de-focus itself when told to, the group 
  245. would be corrupted with what would appear to be two focused views, even 
  246. though only one view can be focused at a time.  
  247. The group assumes that state changes (like from focused to not 
  248. focused) will be successful.  This is in sync with the Turbo Vision 
  249. program model, which assumes that you can always switch between non-
  250. modal views in an application.  This also greatly simplifies the logic 
  251. of focus changes and other operations that change a view's state, 
  252. because we don't have to worry about how to "back up" if a state change 
  253. failed late in a focus change sequence.
  254. Tracing the SetStates
  255. Let's call the first input line which is losing focus View A, and 
  256. the input line that's receiving focus View B.  TGroup.SetCurrent calls 
  257. ViewA^.SetState(sfFocused, False).  TView.SetState sends a 
  258. cmReleasedFocus broadcast event to the view's owner (the dialog) via the 
  259. Message function.  The Message function takes an event as parameters and 
  260. pipes it directly into the destination object's HandleEvent method.
  261. The dialog's HandleEvent receives a cmReleasedFocus broadcast 
  262. event.  By default, TDialog doesn't do anything with the event, and none 
  263. of the ancestral HandleEvents do anything with it either.  This message 
  264. is mainly to inform whoever might care that the view is releasing focus.  
  265. When the event has run its course through the dialog's subviews, the 
  266. Message function will return control to the TView.SetState which called 
  267. it.
  268. Control returns to TGroup.SetCurrent, which next calls 
  269. ViewA^.SetState(sfSelected, False).  TView.SetState simply clears the 
  270. sfSelected flag in its State field, but some TView descendents (such as 
  271. TButton) may redraw themselves to reflect different colors for selected 
  272. vs non selected states.
  273. Control returns to SetCurrent, which next calls 
  274. ViewB^.SetState(sfSelected, True).  The sfSelected flag is set in ViewB, 
  275. which may cause a redraw to reflect  colors of the new state.
  276. Control returns to SetCurrent, which last calls 
  277. ViewB^.SetState(sfFocused, True).  TView.SetState sends a 
  278. cmReceivedFocus broadcast event to its owner-dialog via the Message 
  279. function.  The dialog receives the event in its HandleEvent, but like 
  280. the cmReleasedFocus broadcast, this broadcast event is mainly for 
  281. informational purposes and is not used by any of the inherited 
  282. HandleEvents.
  283. When control again returns to SetCurrent, the group's Current 
  284. pointer is set to point to ViewB.  The focus change sequence is 
  285. complete.
  286. Summary
  287. Turbo Vision assumes that state changes, particularly selection 
  288. and focus state changes, will succeed when invoked.  This section has 
  289. stepped through a focus change sequence to illustrate who all the 
  290. players are behind the scenes and show the roles they play.  Attempting 
  291. to interrupt or short-circuit a focus change will leave the dialog in an 
  292. unstable, probably unusable state.
  293.  
  294.  
  295.  
  296. Demo Programs
  297. So you don't go away empty handed, here are two very simple demo 
  298. programs that illustrate how the interplay between certain TView and 
  299. TGroup methods can be used to perform data validation.  These examples 
  300. are primarily to show where you can plug in validation code - not how to 
  301. validate your data.
  302. As-You-Leave Validation
  303. The Valid1.pas program on the disk accompanying this paper 
  304. implements data validation code in a TInputLine descendent that checks 
  305. the data when the input line loses focus.  If the data is unacceptable, 
  306. the input line is redrawn in red.  It will get the user's attention, but 
  307. will not interfere with what he is doing.
  308. TValidInputLine has three main parts, and all of them are very 
  309. small:  a Valid function to test the data, a SetState procedure to call 
  310. Valid when the view loses focus, and a GetPalette function to map an 
  311. error color (flashing white on red) into the standard TInputline color 
  312. palette when the input line is in an error condition.  All these methods 
  313. are extending inherited ancestor methods and behaviors.  TValidInputLine 
  314. also has a new boolean field, IsValid, to keep track of what the result 
  315. of the last Valid check was and to determine whether GetPalette returns 
  316. an error color palette or its standard color palette.  As soon as the 
  317. error color is no longer needed or wanted, IsValid is set to True, which 
  318. will cause the standard color palette to be used the next time the view 
  319. is drawn.
  320. TValidInputLine looks like this:
  321. type
  322.     TValidInputLine = object(TInputLine)
  323.         IsValid: Boolean;
  324.         constructor Init(var Bounds: TRect; 
  325.                                                  AMaxLen:Integer);
  326.         function GetPalette: PPalette;  virtual;
  327.         procedure SetState(AState: Word;
  328.                                              Enable: Boolean);  virtual;
  329.         function Valid(Command: Word): Boolean; virtual;
  330.     end;
  331. constructor TValidInputLine.Init(var Bounds: TRect;
  332.                                                                 AMaxLen: Integer);
  333. begin
  334.   TInputLine.Init(Bounds, AMaxLen);
  335.     IsValid := Valid(cmOk);
  336. end;
  337. function TValidInputLine.GetPalette: PPalette;
  338. const  AltPalette: String[Length(CInputLine)] 
  339.                                     = CInputLine;
  340. begin
  341.   AltPalette[1] := #255;
  342.     if IsValid then
  343.         GetPalette := TInputLine.GetPalette
  344.     else
  345.         GetPalette := @AltPalette;
  346. end;
  347. procedure TValidInputLine.SetState(AState: Word;
  348.                                                                Enable: Boolean);
  349. begin
  350.     if ((AState and sfFocused) <> 0) and
  351.              GetState(sfFocused) then
  352.         Valid(cmOk);
  353.     TInputLine.SetState(AState, Enable);
  354. end;
  355. function TValidInputLine.Valid(Command: Word): Boolean;
  356. begin
  357.   if Command <> cmCancel then
  358.     begin
  359.         IsValid := (Data^ = 'Hello');
  360.         Valid := IsValid;
  361.         Write(#7);  { beep }
  362.         DrawView;   { Show new colors }
  363.     end;
  364. end;
  365. The Init constructor initializes IsValid by calling the Valid 
  366. method.  Whenever the view loses focus, the SetState method calls Valid, 
  367. which checks the string data of the input line against the only valid 
  368. response - 'Hello'.  IsValid is set and DrawView is called to display 
  369. the new colors.  DrawView will use GetPalette in the process of 
  370. redisplaying the input line, and GetPalette knows to check IsValid to 
  371. see which palette should be returned.
  372. Compile and run the simple shell program and dialog that is 
  373. Valid1.pas.  You will notice the input line is immediately red, because 
  374. the Init constructor calls Valid and the string that's in the input line 
  375. is 'Phone home.' not the 'Hello' that Valid is looking for.  When you 
  376. begin to type, the input line returns to its normal colors.  When you 
  377. Tab to the Ok button, Valid is called and the error color may be 
  378. redisplayed, depending upon what you typed.
  379. Selecting the Cancel button, pressing Escape, or clicking on the 
  380. close icon of the dialog will all cancel the dialog with no further 
  381. validation.  Selecting the Ok button or pressing Enter will close the 
  382. dialog with a cmOk command, and a final round of validation is done 
  383. before the dialog closes.  If the input line is not valid, the dialog 
  384. will not close.  The user must either correct the data or cancel the 
  385. dialog.
  386. When-You're-Done Validation
  387. Turbo Vision dialogs already verify that all their controls return 
  388. Valid true before the dialog is allowed to close.  Valid2.pas selects 
  389. the first control that returned Valid false, so the user is placed on 
  390. the control with bad data.  Valid2.pas also displays the control in the 
  391. red error color.
  392. Valid2.pas uses a similar TValidInputLine object:  the only things 
  393. that are different in Valid2 are that the SetState method was dropped 
  394. and a HandleEvent method was added, and the DrawView call was removed 
  395. from the Valid method.
  396. Valid2 adds a new dialog object type, TDemoDialog, which has 
  397. simply a new Valid method which overrides the inherited TDialog.Valid 
  398. method.  This new method finds the first control whose Valid method 
  399. returns false, and selects that control to make it the currently focused 
  400. view.
  401. Compile and run Valid2.pas.  Tab around the dialog a bit, then 
  402. select the Ok button.  The dialog beeps as the input line's valid method 
  403. objects to closing, the input line is painted red, and the input line 
  404. becomes the focused view.  When you begin typing into the input line or 
  405. Tab to another view, the input line is redisplayed in its normal colors.
  406.  
  407. End.
  408. Though extremely simple, these two programs should provide a 
  409. breadboard upon which you can build and test much more elaborate data 
  410. validation routines of your own.  Armed with some additional knowledge 
  411. of Turbo Vision's internals, your data validation routines can also work 
  412. with Turbo Vision, instead of against it, for greater longevity of the 
  413. program and programmer.
  414.  
  415.  
  416.  
  417. Appendix
  418. Terminology
  419. Let's review some Turbo Vision terms.  A view is any TView object 
  420. or any TView descendent object, including TGroup, TWindow, TDialog, 
  421. TButton, TInputline, etc.  The simplest displayable object in Turbo 
  422. Vision is a TView, and everything visible on the screen is managed by an 
  423. object which is in some way descended from TView.  
  424. Similarly, a group is a TGroup or any descendent of a TGroup, such 
  425. as a TWindow or TDialog.  A group is a view which maintains a list of 
  426. other views which the group is said to "own."  Views are inserted into 
  427. groups, groups maintain a coordinate system relative to the group's 
  428. rectangle origin, and groups distribute events to their subviews in an 
  429. order that's determined by the type of event.
  430. The focused view is the view which receives focused events like 
  431. keystrokes and command events.  There can be only one focused view in an 
  432. application at any time.  
  433. A selected view is the view in a group which the group's Current 
  434. pointer is pointing to.  Each group can have only one selected view, but 
  435. since the application can contain many groups, there can be many 
  436. selected views in an application at one time.  When a group receives 
  437. focus, it gives the focus to its selected view.  When a view loses focus 
  438. to a view in a different group, the view retains its selected state in 
  439. its group but relinquishes its focused state.  
  440. A focused view will always also be selected.  However, a view can 
  441. be selected without being focused.  Basically, the selected state is a 
  442. place holder for which view in a particular group should receive focus 
  443. when focus returns to that group.  
  444. Most of the behavior of a dialog is inherited from the TGroup 
  445. object type, so many of the methods discussed will be described as 
  446. TGroup methods, not TDialog methods.  Understanding what parts of an 
  447. object are inherited, and therefore shared by all descendents of an 
  448. ancestor, is important to get the most out of Turbo Vision.  When you 
  449. want a dialog that has a slightly different Execute method, for example, 
  450. its useful to know that the default Execute method is inherited from 
  451. TGroup, but when you actually implement the new Execute method, it will 
  452. be in a TDialog descendent object.  
  453.  
  454.  
  455. Acknowledgements
  456.  
  457. Chuck Jazdzewski, for guidance.
  458. Eberhard Waiblinger, for time.
  459. Kurt Diesch, for questions.
  460.  
  461.  
  462. 2
  463.  
  464.  
  465.